package com.lt.base;

import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
import org.springframework.data.mongodb.core.aggregation.MatchOperation;
import org.springframework.data.mongodb.core.aggregation.ProjectionOperation;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Field;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;

import com.mongodb.BasicDBObject;

/**
 * mongodb数据库访问共有类
 * 
 * @author laizs
 * @time 2016年4月5日下午2:19:15
 * @file MongoGenDao.java
 */
public abstract class MongoGenDao<T> {
	private final static Logger logger = LoggerFactory.getLogger(MongoGenDao.class);
	@Autowired
	public MongoTemplate mongoTemplate;

	/**
	 * 为属性自动注入bean服务
	 * 
	 * @param mongoTemplate
	 */
	public void setMongoTemplate(MongoTemplate mongoTemplate) {
		this.mongoTemplate = mongoTemplate;
	}

	/**
	 * 保存一个对象
	 * 
	 * @param t
	 */
	public void save(T t) {
		this.mongoTemplate.save(t);
		logger.info("[mongo dao] save:" + t);
	}

	/**
	 * 根据id获取
	 * 
	 * @param id
	 * @return
	 */
	public T queryById(String id) {
		Query query = new Query();
		Criteria criteria = Criteria.where("id").is(id);
		query.addCriteria(criteria);
		logger.info("[mongo dao] queryById:" + query);
		return (T) this.mongoTemplate.findOne(query, getEntityClass());
	}

	/**
	 * 根据id获取
	 * 
	 * @param id
	 * @param include 包括字段
	 * @param ninclude 不包括字段
	 * @return
	 */
	public T queryById(String id, String[] include, String[] ninclude) {
		Query query = new Query();
		setField(query.fields(), include, ninclude);
		Criteria criteria = Criteria.where("id").is(id);
		query.addCriteria(criteria);
		return (T) this.mongoTemplate.findOne(query, getEntityClass());
	}

	/**
	 * 设置查询字段
	 * 
	 * @param field
	 * @param include 包括字段
	 * @param ninclude 不包括字段
	 */
	private void setField(Field field, String[] include, String[] ninclude) {
		if (include != null && include.length > 0) {
			for (String key : include) {
				field.include(key);
			}
		} else if (ninclude != null && ninclude.length > 0) {
			for (String key : ninclude) {
				field.exclude(key);
			}
		} else {
			throw new NullPointerException();
		}
	}

	public static void main(String[] args) {
	}
	/**
	 * 根据条件查询集合
	 * 
	 * @param criteria 查询条件
	 * @param sort 排序
	 * @return
	 */
	public List<T> queryList(Criteria criteria, Sort sort) {
		Query query = new Query(criteria);
		if (sort != null) {
			query.with(sort);
		}
		logger.info("[mongo dao] queryList:" + query);
		return this.mongoTemplate.find(query, getEntityClass());
	}

	/**
	 * 根据条件查询集合
	 * 
	 * @param criteria 查询条件
	 * @param sort 排序
	 * @param include 包括字段
	 * @param ninclude 不包括字段
	 * @return
	 */
	public List<T> queryList(Criteria criteria, Sort sort, String[] include, String[] ninclude) {
		Query query = new Query();
		if (criteria != null) {
			query.addCriteria(criteria);
		}
		if (sort != null) {
			query.with(sort);
		}
		setField(query.fields(), include, ninclude);
		logger.info("[mongo dao] queryList:" + query);
		return this.mongoTemplate.find(query, getEntityClass());
	}

	/**
	 * 根据条件查询集合
	 * 
	 * @param criteria 查询条件
	 * @param page 分页
	 * @param sort 排序
	 * @return
	 */
	public List<T> queryPage(Criteria criteria, Pageable page, Sort sort) {
		if (page == null) {
			throw new NullPointerException();
		}
		Query query = new Query(criteria).with(page);
		if (null != sort) {
			query.with(sort);
		}
		logger.info("[mongo dao] queryList:" + query);
		return this.mongoTemplate.find(query, getEntityClass());
	}

	/**
	 * 根据条件查询集合
	 * 
	 * @param criteria 查询条件
	 * @param page 分页
	 * @param sort 排序
	 * @param include 包括字段
	 * @param ninclude 不包括字段
	 * @return
	 */
	public List<T> queryPage(Criteria criteria, Pageable page, Sort sort, String[] include, String[] ninclude) {
		if (page == null) {
			throw new NullPointerException();
		}
		Query query = new Query(criteria).with(page);
		if (null != sort) {
			query.with(sort);
		}
		setField(query.fields(), include, ninclude);
		logger.info("[mongo dao] queryList:" + query);
		return this.mongoTemplate.find(query, getEntityClass());
	}

	/**
	 * 根据条件查询单个实体
	 * 
	 * @param criteria 查询条件
	 * @return
	 */
	public T queryOne(Criteria criteria) {
		Query query = new Query(criteria);
		logger.info("[mongo dao] queryOne:" + query);
		return (T) this.mongoTemplate.findOne(query, getEntityClass());
	}

	/**
	 * 根据条件查询单个实体
	 * 
	 * @param criteria 查询条件
	 * @param include 包括字段
	 * @param ninclude 不包括字段
	 * @return
	 */
	public T queryOne(Criteria criteria, String[] include, String[] ninclude) {
		Query query = new Query(criteria);
		setField(query.fields(), include, ninclude);
		logger.info("[mongo dao] queryOne:" + query);
		return (T) this.mongoTemplate.findOne(query, getEntityClass());
	}

	/**
	 * 根据分页条件查询
	 * 
	 * @param query
	 * @param start 查询起始值
	 * @param size 分页大小
	 * @return
	 */
	public List<T> getPage(Criteria criteria, int start, int size, Sort sort) {
		Query query = new Query();
		query.skip(start);
		query.limit(size);
		if (null != sort) {
			query.with(sort);
		}
		logger.info("[Mongo Dao]queryPage:" + query + "(" + start + "," + size + ")");
		return this.mongoTemplate.find(query, getEntityClass());
	}

	/**
	 * 根据条件查询库中符合记录的总数,为分页查询服务
	 * 
	 * @param query
	 * @return
	 */
	public Long getPageCount(Criteria criteria) {
		Query query = new Query(criteria);
		logger.info("[Mongo Dao]getPageCount:" + query);
		return this.mongoTemplate.count(query, getEntityClass());
	}

	/**
	 * 根据id删除
	 * 
	 * @param id
	 */
	public void deleteById(String id) {
		Criteria criteria = Criteria.where("_id").in(id);
		if (null != criteria) {
			logger.info("[Mongo Dao] deleteById:" + id);
			if (this.queryOne(criteria) != null) {
				this.mongoTemplate.remove(new Query(criteria), getEntityClass());
			}
		}
	}

	/**
	 * 删除对象
	 * 
	 * @param t
	 */
	public void delete(T t) {
		logger.info("[Mongo Dao] t:" + t);
		this.mongoTemplate.remove(t);
	}

	/**
	 * 删除对象
	 * 
	 * @param t
	 */
	public void delete(Criteria criteria) {
		Query query = new Query(criteria);
		logger.info("[Mongo Dao] query:" + query);
		this.mongoTemplate.remove(query, getEntityClass());
	}

	/**
	 * 修改满足条件的多条记录
	 * 
	 * @param query
	 * @param update
	 */
	public void update(Criteria criteria, Update update) {
		Query query = new Query(criteria);
		logger.info("[Mongo Dao ]updateMulti:query(" + query + "),update(" + update + ")");
		this.mongoTemplate.updateMulti(query, update, getEntityClass());
	}


	/**
	 * 修改,如果要修改的对象不存在则添加
	 * 
	 * @param query
	 * @param update
	 */
	public void updateInser(Criteria criteria, Update update) {
		Query query = new Query(criteria);
		logger.info("[Mongo Dao ]updateInser:query(" + query + "),update(" + update + ")");
		this.mongoTemplate.upsert(query, update, this.getEntityClass());
	}

	/**
	 * 分组，统计个数
	 * 
	 * @param criteria 分组查询条件
	 * @param groupByField 分组字段
	 * @return
	 */
	public List<BasicDBObject> groupByCount(Criteria criteria, String[] groupByField) {
		MatchOperation match = Aggregation.match(criteria);
		Aggregation group = null;
		if (groupByField.length == 1) {
			ProjectionOperation project = Aggregation.project("count").and(groupByField[0]).previousOperation();
			group = Aggregation.newAggregation(match, Aggregation.group(groupByField).count().as("count"), project);
		} else {
			group = Aggregation.newAggregation(match, Aggregation.group(groupByField).count().as("count"));
		}

		AggregationResults<BasicDBObject> result = mongoTemplate.aggregate(group,
				this.getEntityClass(), BasicDBObject.class);
		return result.getMappedResults();
	}

	/**
	 * 分组，汇总
	 * 
	 * @param criteria 分组查询条件
	 * @param sumField sum字段
	 * @param groupByField 分组字段
	 * @return
	 */
	public List<BasicDBObject> groupBySum(Criteria criteria, String sumField, String[] groupByField) {
		MatchOperation match = Aggregation.match(criteria);
		Aggregation group = null;
		if (groupByField.length == 1) {
			ProjectionOperation project = Aggregation.project("sum").and(groupByField[0]).previousOperation();
			group = Aggregation.newAggregation(match, Aggregation.group(groupByField).sum(sumField).as("sum"), project);
		} else {
			group = Aggregation.newAggregation(match, Aggregation.group(groupByField).sum(sumField).as("sum"));
		}

		AggregationResults<BasicDBObject> result = mongoTemplate.aggregate(group,
				this.getEntityClass(), BasicDBObject.class);
		return result.getMappedResults();
	}

	/**
	 * 分组，平均
	 * 
	 * @param criteria 分组查询条件
	 * @param sumField sum字段
	 * @param groupByField 分组字段
	 * @return
	 */
	public List<BasicDBObject> groupByAvg(Criteria criteria, String sumField, String[] groupByField) {
		MatchOperation match = Aggregation.match(criteria);
		Aggregation group = null;
		if (groupByField.length == 1) {
			ProjectionOperation project = Aggregation.project("avg").and(groupByField[0]).previousOperation();
			group = Aggregation.newAggregation(match, Aggregation.group(groupByField).avg(sumField).as("avg"), project);
		} else {
			group = Aggregation.newAggregation(match, Aggregation.group(groupByField).avg(sumField).as("avg"));
		}

		AggregationResults<BasicDBObject> result = mongoTemplate.aggregate(group,
				this.getEntityClass(), BasicDBObject.class);
		return result.getMappedResults();
	}

	/**
	 * 分组，最大
	 * 
	 * @param criteria 分组查询条件
	 * @param sumField sum字段
	 * @param groupByField 分组字段
	 * @return
	 */
	public List<BasicDBObject> groupByMax(Criteria criteria, String sumField, String[] groupByField) {
		MatchOperation match = Aggregation.match(criteria);
		Aggregation group = null;
		if (groupByField.length == 1) {
			ProjectionOperation project = Aggregation.project("max").and(groupByField[0]).previousOperation();
			group = Aggregation.newAggregation(match, Aggregation.group(groupByField).max(sumField).as("max"), project);
		} else {
			group = Aggregation.newAggregation(match, Aggregation.group(groupByField).max(sumField).as("max"));
		}

		AggregationResults<BasicDBObject> result = mongoTemplate.aggregate(group,
				this.getEntityClass(), BasicDBObject.class);
		return result.getMappedResults();
	}

	/**
	 * 分组，最小
	 * 
	 * @param criteria 分组查询条件
	 * @param sumField sum字段
	 * @param groupByField 分组字段
	 * @return
	 */
	public List<BasicDBObject> groupByMin(Criteria criteria, String sumField, String[] groupByField) {
		MatchOperation match = Aggregation.match(criteria);
		Aggregation group = null;
		if (groupByField.length == 1) {
			ProjectionOperation project = Aggregation.project("min").and(groupByField[0]).previousOperation();
			group = Aggregation.newAggregation(match, Aggregation.group(groupByField).min(sumField).as("min"), project);
		} else {
			group = Aggregation.newAggregation(match, Aggregation.group(groupByField).min(sumField).as("min"));
		}
		AggregationResults<BasicDBObject> result = mongoTemplate.aggregate(group, this.getEntityClass(), BasicDBObject.class);
		return result.getMappedResults();
	}


	/**
	 * 钩子方法,由子类实现返回反射对象的类型
	 * 
	 * @return
	 */
	protected abstract Class<T> getEntityClass();


}